package com.ssm.aop;

import com.alibaba.fastjson.JSON;
import com.ssm.annotation.SysLogMethod;
import com.ssm.pojo.SysLog;
import com.ssm.pojo.User;
import com.ssm.service.SysLogService;
import com.ssm.shrio.ShiroUtils;
import com.ssm.util.HttpContextUtils;
import com.ssm.util.IPUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;

/**
 * Created by 墨殇
 * 2017/12/27.
 * @author Administrator
 */
@Aspect
@Component
public class SystemLogAspect {
//注入Service用于把日志保存数据库
      @Resource
      private SysLogService SysLogService;

              private  static  final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);

              //Controller层切点
              @Pointcut("execution (* com.ssm.controller..*.*(..))")
      public  void controllerAspect() {
             }

              /**
        * 前置通知 用于拦截Controller层记录用户的操作
        *
        * @param joinPoint 切点
        */
              @Before("controllerAspect()")
      public void doBefore(JoinPoint joinPoint) {
                 System.out.println("==========执行controller前置通知===============");
                 if(logger.isInfoEnabled()){
                         logger.info("before " + joinPoint);
                     }
             }
//
//              //配置controller环绕通知,使用在方法aspect()上注册的切入点
//                @Around("controllerAspect()")
//        public void around(JoinPoint joinPoint){
//                   System.out.println("==========开始执行controller环绕通知===============");
//                   long start = System.currentTimeMillis();
//                   try {
//                           ((ProceedingJoinPoint) joinPoint).proceed();
//                           long end = System.currentTimeMillis();
//                           if(logger.isInfoEnabled()){
//                                   logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
//                               }
//                           System.out.println("==========结束执行controller环绕通知===============");
//                       } catch (Throwable e) {
//                           long end = System.currentTimeMillis();
//                           if(logger.isInfoEnabled()){
//                                   logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
//                               }
//                       }
//               }

              /**
        * 后置通知 用于拦截Controller层记录用户的操作
        *
        * @param joinPoint 切点
        */
              @After("controllerAspect()")
      public  void after(JoinPoint joinPoint) {
                  HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

                  HttpSession session = request.getSession();
                  User user = (User) session.getAttribute("user");

                  try {

                         String targetName = joinPoint.getTarget().getClass().getName();
                         String methodName = joinPoint.getSignature().getName();
                         Object[] arguments = joinPoint.getArgs();
                         Class targetClass = Class.forName(targetName);
                         Method[] methods = targetClass.getMethods();
                         String operationType = "";
                         String operationName = "";
                          for (Method method : methods) {
                                  if (method.getName().equals(methodName)) {
                                         Class[] clazzs = method.getParameterTypes();
                                          if (clazzs.length == arguments.length) {
                                                  operationType = method.getAnnotation(SysLogMethod.class).operationType();
                                                  operationName = method.getAnnotation(SysLogMethod.class).operationName();
                                                  break;
                                             }
                                     }
                             }
                         //*========控制台输出=========*//
                         System.out.println("=====controller后置通知开始=====");
                         System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);
                         System.out.println("方法描述:" + operationName);
                         System.out.println("请求人:" + user.getUserName());
                         System.out.println("请求IP:" + IPUtils.getIpAddr(request));
                         //*========数据库日志=========*//


                      //请求的参数
                      Object[] args = joinPoint.getArgs();
                      String params = JSON.toJSONString(args[0]);
                      SysLog sysLog=new SysLog();
                      //设置IP地址
                      sysLog.setIp(IPUtils.getIpAddr(request));

                      sysLog.setRemark(operationName);
                         sysLog.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);
                      sysLog.setParams(params);
                       sysLog.setOperationor(user.getUserName());
                       sysLog.setOperation(operationType);
                         sysLog.setCreateBy(user.getUserId());
                         sysLog.setUpdateBy(user.getUserId());
                         sysLog.setCreateName(user.getUserName());
                         sysLog.setUpdateName(user.getUserName());
                         sysLog.setIsSuccess( getControllerMethodLogContent(joinPoint).getIsSuccess());
                         sysLog.setRemark(sysLog.getRemark().concat(   getControllerMethodLogContent(joinPoint).getRemark()));
                       sysLog.setStatus(1);
                         //保存数据库
                         SysLogService.add(sysLog);
                         System.out.println("=====controller后置通知结束=====");
                     }  catch (Exception e) {
                         //记录本地异常日志
                         logger.error("==后置通知异常==");
                         logger.error("异常信息:{}", e.getMessage());
                     }
             }

             //配置后置返回通知,使用在方法aspect()上注册的切入点
               @AfterReturning("controllerAspect()")
       public void afterReturn(JoinPoint joinPoint){
                   System.out.println("=====执行controller后置返回通知=====");
                       if(logger.isInfoEnabled()){
                               logger.info("afterReturn " + joinPoint);
                           }
               }

             /**
       * 异常通知 用于拦截记录异常日志
       *
       * @param joinPoint
       * @param e
       */
              @AfterThrowing(pointcut = "controllerAspect()", throwing="e")
      public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
                 /*HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
         HttpSession session = request.getSession();
         //读取session中的用户
         User user = (User) session.getAttribute(WebConstants.CURRENT_USER);
         //获取请求ip
         String ip = request.getRemoteAddr(); */
                 //获取用户请求方法的参数并序列化为JSON格式字符串

                  User user = ShiroUtils.getUserEntity();
                  //获取request
                  HttpServletRequest request = HttpContextUtils.getHttpServletRequest();

                 String params = "";
                  if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {
                          for ( int i = 0; i < joinPoint.getArgs().length; i++) {
                                 params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
                             }
                     }
                  try {

                          String targetName = joinPoint.getTarget().getClass().getName();
                          String methodName = joinPoint.getSignature().getName();
                          Object[] arguments = joinPoint.getArgs();
                          Class targetClass = Class.forName(targetName);
                          Method[] methods = targetClass.getMethods();
                          String operationType = "";
                          String operationName = "";
                           for (Method method : methods) {
                                   if (method.getName().equals(methodName)) {
                                          Class[] clazzs = method.getParameterTypes();
                                           if (clazzs.length == arguments.length) {
                                                   operationType = method.getAnnotation(SysLogMethod.class).operationType();
                                                   operationName = method.getAnnotation(SysLogMethod.class).operationName();
                                                   break;
                                              }
                                      }
                              }
                          /*========控制台输出=========*/
                         System.out.println("=====异常通知开始=====");
                         System.out.println("异常代码:" + e.getClass().getName());
                         System.out.println("异常信息:" + e.getMessage());
                         System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);
                         System.out.println("方法描述:" + operationName);
                         System.out.println("请求人:" + user.getUserName());
                         System.out.println("请求IP:" + IPUtils.getIpAddr(request));
                         System.out.println("请求参数:" + params);
                            /*==========数据库日志=========*/
                      SysLog sysLog=new SysLog();

                      //请求的参数
                      Object[] args = joinPoint.getArgs();
                      String params1 = JSON.toJSONString(args[0]);

                      //设置IP地址
                      sysLog.setIp(IPUtils.getIpAddr(request));

                      sysLog.setRemark(operationName);
                      sysLog.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);
                      sysLog.setParams(params);
                      sysLog.setOperationor(user.getUserName());
                      sysLog.setOperation(operationType);
                      sysLog.setCreateBy(user.getUserId());
                      sysLog.setUpdateBy(user.getUserId());
                      sysLog.setCreateName(user.getUserName());
                      sysLog.setUpdateName(user.getUserName());
                      sysLog.setStatus(1);
                      //保存数据库
                      SysLogService.add(sysLog);
                         System.out.println("=====异常通知结束=====");
                     }  catch (Exception ex) {
                         //记录本地异常日志
                         logger.error("==异常通知异常==");
                         logger.error("异常信息:{}", ex.getMessage());
                     }
                  /*==========记录本地异常日志==========*/
                 logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);

             }

    /**
     * 获取注解中对方法日志返回信息
     * @return
     * @throws Exception
     */
    public static SysLog getControllerMethodLogContent(JoinPoint joinPoint) throws Exception{

        //获取类名
        String targetName = joinPoint.getTarget().getClass().getName();

        //获取方法名
        String methodName = joinPoint.getSignature().getName();

        //获取参数
        Object[] arguments = joinPoint.getArgs();

        //反射获取类的字节码文件
        Class targetClass = Class.forName(targetName);

        //获取拦截类中的所有方法
        Method[] methods = targetClass.getMethods();

        SysLog log = null;

        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                //获取参数类型
                Class<?>[] clazzs = method.getParameterTypes();

                if (clazzs.length == arguments.length) {

                    log = (SysLog) arguments[clazzs.length - 1];
                    break;
                }
            }
        }

        return log;
    }
}
